Terraform の ステートファイルを Cloud Storage で管理する #cm_google_cloud_adcal_2024
はじめに
クラスメソッドの Google Cloud Advent Calendar 2024 の 5 日目のブログです!
Google Cloud Advent Calendar 2024 では Google Cloud が大好きな弊社エンジニアが技術ブログを持ち回りで毎日執筆中ですので、ご覧になっていただけると嬉しいです。
本日は Cloud Storage を利用した Terraform ステートファイル管理 について解説いたします。
ステートファイルと管理の課題について
Terraform を実行してリソースを作成すると、どのようなリソースを Terraform で作成したかの情報が ステートファイル に保存されます。デフォルトでステートファイルは、 Terraform を実行したディレクトリに terraform.tfstate
というファイル名で作成されます。Terraform を実行する度に、変更した Terraform コードとステートファイルを比較し、追加 / 変更 / 削除するリソースを判断しています。
個人で Terraform を実行する分には、ステートファイルはローカル環境に保存しておけば特段課題はありません。しかし、チーム開発のケースにおいてはステートファイルの管理において以下のような考慮をする必要があります。
- 複数メンバーが同じステートファイルを共有できるようにする
- 複数メンバーが 同時に Terraform を実行した際にステートファイルの更新や参照で競合が起きないようステートファイルをロックする仕組みを導入する
ステートファイルの管理における課題や管理方法の選択肢については以下ブログでも紹介していますのでご参照ください。
Cloud Storage でステートファイルを管理する
Google Cloud リソースを Terraform で作成するケースにおいて、 Cloud Storage でステートファイルを管理することで以下のメリットを享受できます。
- IAM で管理されたプリンシパルと IAM ロールによる認証/認可により、Google Cloud プロジェクト内の開発チームでのファイル共有を容易に実現できる
- Terraform は Cloud Storage 単体でのステートファイルのロック機能を提供しているため、Amazon S3 でステートファイルを管理する際に必要であった DynamoDB を利用したロック機能の構成を準備しなくてよい(※)
※ Terraform 1.10 より experimental な機能として Amazon S3 の "lockfile" を利用したロックが提供されました。DynamoDB を利用したロックのための構成は将来的に不要となりそうです。詳細は以下ご参照ください。
Locking can be enabled via an S3 "lockfile" (introduced as experimental in Terraform 1.10) or DynamoDB. To support migration from older versions of Terraform which only support DynamoDB-based locking, the S3 and DynamoDB arguments below can be configured simultaneously. In a future minor version the DynamoDB locking mechanism will be removed.
Amazon S3 と DynamoDB を組み合わせたステートファイルの管理とロックのための構成については以下をご参照ください。
やってみる
Cloud Shell には Terraform がプリインストールされていますので、本検証では Cloud Shell から Terraform を実行していきます。検証の中で、ステートファイルを Cloud Storage で管理するための設定や挙動について確認してみたいと思います。
Terraform で作成するリソースについては、VPC(default) に Compute Engine VM インスタンスを起動する簡易的な構成とします。
また、説明のしやすさを勘案して以下の流れで検証を実施します。
- Cloud Storage バケットは事前に Cloud SDK (gcloud コマンド)を利用して作成
- Terraform 設定ファイルにバックエンド(ステートファイル管理方法の設定)として Cloud Storage バケットを指定
- Terraform を実行しステートファイルの参照やロックの挙動を確認
実際の運用ではステート管理用の Cloud Storage バケットも Terraform で一元管理するのがよいでしょう。詳細は以下をご参照ください。
権限について
このあとの手順で、ステートファイルを管理するための Cloud Storage バケットを作成しますが、Cloud Storage バケットの作成には ストレージ管理者 (roles/storage.admin
) の IAM ロールが必要です。
Cloud Shell から Terraform を実行すると、デフォルトでは実行した IAM ユーザに付与された権限で Terraform から各種リソースを操作します。
そのため、Terraform から Cloud Storage バケットのステートファイルを参照/編集するため Storage オブジェクト管理者(roles/storage.objectAdmin
) の権限、Terraform で Compute Engine VM インスタンスを作成するための Compute インスタンス管理者(v1) (roles/compute.instanceAdmin.v1
) の IAM ロールが必要となります。
ただし、本検証ではオーナー権限で手順を進めることとします。
API を有効化する
Cloud Storage バケットの作成と Terraform による Compute Engine VM インスタンスの作成のため、以下 API の有効化が必要となります。
- Compute Engine API
- Cloud Storage API
有効化されていない場合、Cloud Shell より必要な API の有効化をします。
$ gcloud services enable compute.googleapis.com
$ gcloud services enable storage.googleapis.com
検証用 Terraform 設定ファイルを用意する
検証用の Terraform 設定ファイル (provider.tf
, main.tf
, variables.tf
) を Cloud Shell 上に用意し gs-state-test
ディレクトリに配置しておきます。
$ mkdir gs-state-test
$ cd gs-state-test
$ ls
main.tf provider.tf variables.tf
provider "google" {
project = var.project_id
}
resource "google_compute_instance" "default" {
name = "tf-instance"
machine_type = "e2-micro"
zone = "asia-northeast1-a"
boot_disk {
initialize_params {
image = "projects/debian-cloud/global/images/debian-12-bookworm-v20241112"
size = 20
type = "pd-balanced"
}
}
network_interface {
network = "default"
}
}
variable "project_id" {
type = string
}
Cloud Storage バケットを作成する
Cloud Shell から以下コマンドでステートファイル管理用の Cloud Storage バケットを作成します。
$ export PROJECT_ID=$(gcloud config get-value project)
$ gcloud storage buckets create gs://gs-state-test-${PROJECT_ID}/ \
--location=asia-northeast1 \
--public-access-prevention
Cloud Storage を Terraform のバックエンドに指定
Terraform のバックエンドとは、ステートファイルの保管方法に関する設定をす項目です。デフォルトだとローカルに保存するローカルバックエンドの動作となりますが、Cloud Storage や S3 にステートファイルを保存する場合、リモートバックエンドとして保管先を指定します。
main.tf
に以下を追加します。
terraform {
backend "gcs" {
bucket = "<BUCKET_NAME>"
prefix = "terraform/state"
}
}
~~~
Terraform 自体の設定であるため terraform
ブロックにバックエンド設定を記述します。
backend
: バックエンドの名前を指定します。
bucket
: Cloud Storage バケットを指定します。今回の場合、"gs-state-test-<PROJECT_ID>" を指定しました。
prefix
: バケット内の保存先を指定します。
構文の詳細は以下をご参照ください。
Terraform を実行してみる
まずは Terraform を初期化します。
terraform init
以下のようにバックエンドの設定を初期化するログが表示されました。
Initializing the backend...
Successfully configured the backend "gcs"! Terraform will automatically
use this backend unless the backend configuration changes.
次に Terraform を実行します。
terraform apply
Terraform 実行後、バックエンドとして指定したバケットを確認してみると default.tfstate
というファイル名でステートファイルが生成されていることがわかります。
$ gsutil ls gs://gs-state-test-${PROJECT_ID}/terraform/state
gs://gs-state-test-<PROJECT_ID>/terraform/state/default.tfstate
以下コマンドでステートファイルの内容が確認できます。
$ gsutil cat gs://gs-state-test-${PROJECT_ID}/terraform/state/default.tfstate
ロック動作を試してみる
Terraform 実行時にステートファイルが正しくロックされるか確認してみます。まずは terraform apply
を実行します。
$ terraform apply
上記の Terraform 実行中に別のユーザで Terraform を実行すると以下のようになります。
$ terraform apply
Acquiring state lock. This may take a few moments...
╷
│ Error: Error acquiring the state lock
│
│ Error message: writing "gs://gs-state-test-<PROJECT_ID>/terraform/state/default.tflock" failed: googleapi: Error 412: At least one of the pre-conditions you specified did not hold.,
│ conditionNotMet
│ Lock Info:
│ ID: 1733385132804458
│ Path: gs://gs-state-test-da-<PROJECT_ID>/terraform/state/default.tflock
│ Operation: OperationTypeApply
│ Who: <USER>
│ Version: 1.5.7
│ Created: 2024-12-05 07:52:12.515160731 +0000 UTC
│ Info:
│
│
│ Terraform acquires a state lock to protect the state from being written
│ by multiple users at the same time. Please resolve the issue above and try
│ again. For most commands, you can disable locking with the "-lock=false"
│ flag, but this is not recommended.
Cloud Storage バケットを見てみると、default.tflock
というファイルが生成されていることがわかります。
$ gsutil ls -r gs://gs-state-test-da-${PROJECT_ID}/
~~~
gs://gs-state-test-<PROJECT_ID>/terraform/state/default.tflock
この後、最初のユーザでの terraform apply
をキャンセルし、Cloud Storage バケットを見てみると、default.tflock
というファイルは削除されます。この状態でもう一度別のユーザで terraform apply
を実行すると問題無く実行できます。
最後にステートファイルのロックの仕組みについて補足します。
terraform apply
を実行すると Terraform はバックエンドにロックファイル.tflock
が存在するかを確認します。
ロックファイルが存在しなければ作成し、ステートファイルを参照してリソースをデプロイします。その後、リソースのデプロイが完了すると Terraform はバックエンドからロックファイルを削除します。
バックエンドにロックファイル.tflock
が存在すれば、他ユーザがステートファイルを参照していると判断し、Terraform の実行を中断します。
おわりに
Terraform のステートファイルを Cloud Storage で管理する方法とロック機能の動作について説明いたしました。非常に簡単なステップで利用できることがわかったかと思います。
Google Cloud で Terraform を利用する際のベストプラクティスは公式ドキュメントにもまとまっていますので、興味のある方はご覧になってみてください。
また、今後も Google Cloud x Terraform に関する記事を公開していこうと思いますのでご期待ください。
Google Cloud Advent Calendar 2024 の 明日 12/6 投稿も私 村田一紘 でございます。